#include "StdAfx.h"
#include "WaveSample.h"
//#include <SFML/System/Randomizer.hpp>
#include "Randomizer.h"
#include <math.h>

//static 	sf::Randomizer randomizer;
static Randomizer randomizer;

Sample::Sample(void)
{
	data = NULL;
}

Sample::Sample(int length)
{
	if(length==1024)
	{
		printf("Alloc 2048 bytes");
	}
	this->data = new short[length];
	this->length = length;
	for(int i=0; i<length; i++)
	{
		data[i] = 0;
	}
}

Sample::Sample(short* data, int length)
{
	this->data = data;
	this->length = length;
}

Sample::~Sample(void)
{
	if(data) {
		delete [] data;
		data = NULL;
	}
}

Sample* Sample::Copy(double intensity, int maxVariation)
{
	Sample* sample = new Sample(length);
	register int i = 0;
	for(; i<length; i++)
	{
		/*
		if(maxVariation)
		{
			int add = randomizer.Random(-maxVariation, maxVariation);
			int addedVal = data[i]+add;
			if(addedVal>SHORT_MAX) addedVal = SHORT_MAX;
			else if(addedVal<SHORT_MIN) addedVal = SHORT_MIN;
			sample->data[i] = addedVal;
		}
		*/
		sample->data[i] = (short)((double)data[i]*intensity);
	}
	return sample;
}

Sample* Sample::Append(Sample* sample)
{
	Sample* newSample = new Sample(this->length+sample->length);
	register int i=0, sampleIdx=0;
	for(; i<this->length; i++, sampleIdx++)
	{
		newSample->data[sampleIdx] = this->data[i];
	}
	for(i=0; i<sample->length; i++, sampleIdx++)
	{
		newSample->data[sampleIdx] = sample->data[i];
	}
	return newSample;
}

Sample* Sample::LowPassFilter(float gain, float neg, float pos)
{
	float xv[3], yv[3];
	xv[0] = xv[1] = xv[2] = 0;
	yv[0] = yv[1] = yv[2] = 0;

	Sample* transformed = new Sample(length);
	for (int i=0; i<length; i++)
      { xv[0] = xv[1]; xv[1] = xv[2]; 
        xv[2] = (float)data[i] / gain;
        yv[0] = yv[1]; yv[1] = yv[2]; 
        yv[2] =   (xv[0] + xv[2]) + 2 * xv[1]
                     + ( neg * yv[0]) + (  pos * yv[1]);
        transformed->data[i] = yv[2];
      }
	return transformed;
}

Sample* Sample::LowPass400Filter()
{
//#define GAIN 1.513365061e+03
//#define NEG -0.9286270861
//#define POS 1.9259839697
	return LowPassFilter(1.513365061e+03, -0.9286270861, 1.9259839697);
}

Sample* Sample::LowPass6000Filter()
{
//filter
//#define NZEROS 2
//#define NPOLES 2

//6000Hz
//#define GAIN 1.024264069e+01
//#define NEG -0.3333333333
//#define POS 0.9428090416

//3000Hz
//#define GAIN 3.338387406e+01
//#define NEG -0.5740619151
//#define POS 1.4542435863

//1500Hz
//#define GAIN 1.184456202e+02
//#define NEG -0.7575469445
//#define POS 1.7237761728

//600Hz
/*
#define GAIN 6.847831502e+02
#define NEG -0.8948743446
#define POS 1.8890330794
*/
//400Hz
/*
#define GAIN 1.513365061e+03
#define NEG -0.9286270861
#define POS 1.9259839697
*/
	return LowPassFilter(1.024264069e+01, -0.3333333333, 0.9428090416);
}

void Sample::AddWhiteNoise(double intensity)
{
	if(intensity>0.01)
	{
		for(int i=0; i<this->length; i++)
		{
			//short add = ((double)randomizer.Random(0, SHORT_MAX*2) * intensity)-SHORT_MAX;
			short add = ((double)randomizer.nextShort()*intensity);
			this->data[i] += add;
		}
	}
}

void Sample::AddBrownNoise(double intensity)
{
	if(intensity>0.01)
	{
		int add = 0, lastAdded=0;
		for(int i=0; i<this->length; i++)
		{
			do {
				//short s = randomizer.Random(SHORT_MIN, SHORT_MAX);
				short s = randomizer.nextShort();
				add = lastAdded + s;
			}
			while (add>SHORT_MAX || add<SHORT_MIN);
			double d = add*intensity;
			this->data[i] += d;
			lastAdded = add;
		}
	}
}

WaveSample::WaveSample(void)
{
}

WaveSample::WaveSample(std::string& name, short* sample, int length, int rate, double frequency)
{
	this->data = sample;
	this->length = length;
	this->name.assign(name);
	this->rate = rate;
	this->frequency = frequency;
}

WaveSample::~WaveSample(void)
{
	if(data) {
		delete [] data;
		data = NULL;
	}
	//if(data) delete [] data;
}

void WaveSample::WaveLoop(WaveSample* sample, int phaseCount, void(*WaveLoopCb)(WaveSample*,int,int,int))
{
	int phaseLength = sample->length/phaseCount;
	int phasePos = 0;
	int phase = 0;
	for(int i=0; i<sample->length; i++, phasePos++)
	{
		if(phasePos>=phaseLength)
		{
			phase++;
			phasePos = 0;
		}
		WaveLoopCb(sample, phaseLength, phase, phasePos);
	}
}

WaveSample* WaveSample::CreateSample(std::string& name, double frequency, int rate, void(*WaveSampleCb)(WaveSample*))
{
	int frames = (int)((double)rate)/frequency;
	WaveSample* sample = new WaveSample(name, new short[frames], frames, rate, frequency);
	if(WaveSampleCb) WaveSampleCb(sample);
	return sample;
}

static void TriangularCb(WaveSample* waveSample, int phaseLength, int phase, int phasePos)
{
	double strength = ((double)phasePos/(double)phaseLength)*SHORT_MAX;
	int roundedStrength = (int)strength;
	int add = 0;
	switch(phase) {
		case 0:
			add = roundedStrength;
			break;
		case 1:
			add = SHORT_MAX-roundedStrength;
			break;
		case 2:
			add -= roundedStrength;
			break;
		case 3:
			add = roundedStrength-SHORT_MAX;
			break;
	}
	waveSample->data[(phase*phaseLength)+phasePos] = add;
}

static void SineCb(WaveSample* waveSample)
{
	double pi2oN = 8.0 * atan(1.0) / ((double)waveSample->rate/waveSample->frequency);
	for(int i=0; i<waveSample->length; i++)
	{
		double dval = sin( pi2oN*i );
		waveSample->data[i] = dval*SHORT_MAX;
	}
}

WaveSample* WaveSample::SineSample(std::string& name, double frequency, int rate)
{
	return CreateSample(name, frequency, rate, SineCb);
}

WaveSample* WaveSample::TriangularSample(std::string& name, double frequency, int rate)
{
	WaveSample* sample = CreateSample(name, frequency, rate, NULL);
	WaveLoop(sample, 4, TriangularCb);
	return sample;
}

static void RandomCb(WaveSample* waveSample)
{
	for(int i=0; i<waveSample->length; i++)
	{
		//waveSample->data[i] = randomizer.Random(SHORT_MIN, SHORT_MAX);
		waveSample->data[i] = randomizer.nextShort();
	}
}

WaveSample* WaveSample::RandomSample(std::string& name, double frequency, int rate)
{
	return CreateSample(name, frequency, rate, RandomCb);
}

static void SquareCb(WaveSample* waveSample)
{
	int i=0;
	for(; i<waveSample->length/2; i++)
	{
		waveSample->data[i] = SHORT_MAX;
	}
	for(; i<waveSample->length; i++)
	{
		waveSample->data[i] = SHORT_MIN;
	}
}

WaveSample* WaveSample::SquareSample(std::string& name, double frequency, int rate)
{
	return CreateSample(name, frequency, rate, SquareCb);
}

static void SawtoothCb(WaveSample* waveSample)
{
	int half = waveSample->length/2;
	int i=0;
	for(; i<half; i++)
	{
		double strength = (double)i / (double)half * (double)SHORT_MAX;
		waveSample->data[i] = (short)strength - SHORT_MAX;
	}
	for(; i<waveSample->length; i++)
	{
		double strength = (double)i / (double)half * (double)SHORT_MAX;
		waveSample->data[i] = (short)strength;
	}
}

WaveSample* WaveSample::SawtoothSample(std::string& name, double frequency, int rate)
{
	return CreateSample(name, frequency, rate, SawtoothCb);
}
